/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package univalle.eisc.pais.ai.searcheslab.logic;

import java.util.*;
import univalle.eisc.pais.ai.searcheslab.model.*;

/**
 *
 * @author victor
 */
public class Seeker {

    public static Stage stage;
    public static boolean haveItem1 = false;
    public static boolean haveItem2 = false;
    public static int loadedWeight = 0;
    public static boolean item1Right = false;
    public static boolean item2Right = false;
    public static int expandedNodes;
    public int depthOfTheTree;
    public int computationTime;
    public int totalCost;

    public Seeker() {
        haveItem1 = false;
        haveItem2 = false;
        loadedWeight = 0;
        item1Right = false;
        item2Right = false;
        expandedNodes = 0;
        depthOfTheTree = 0;
        computationTime = 0;
        totalCost = 0;
    }

    private Route getRouteToNode(final Node node) {

        Route route = new Route();
        Node auxNode = node;
        do {
            route.addStep(auxNode);
            auxNode = auxNode.parent;

        } while (auxNode != null);
        return route;

    }

    public Route breadth(Stage stage) {
        long time = System.currentTimeMillis();
        Seeker.stage = stage;
        Route route = new Route();
        Queue<Node> auxQueue = new LinkedList<Node>();
        auxQueue.add(new Node(stage.getRobot().getX(), stage.getRobot().getY()));

        int it = 0;
        boolean sucess = false;
        while (!auxQueue.isEmpty() && !sucess) {
            Node headNode = auxQueue.poll();
            it++;
            boolean yahoo = evaluateState(headNode);
            if (yahoo) { // something happened
                if (sucess = headNode.isGoal()) {
                    route = getRouteToNode(headNode);
                    depthOfTheTree = headNode.deepness;
                    totalCost = headNode.cost;
                }
                auxQueue.clear();
                auxQueue.add(headNode);
            } else {
                auxQueue.addAll(headNode.getOffsprings());
            }
        }
        computationTime = (int) (System.currentTimeMillis() - time);
        printSucess(sucess);

        return route;
    }

    public Route uniformCost(Stage stage) {
        long time = System.currentTimeMillis();
        Seeker.stage = stage;
        Route route = new Route();
        PriorityQueue<Node> auxQueue = new PriorityQueue<Node>();
        auxQueue.add(new Node(stage.getRobot().getX(), stage.getRobot().getY()));

        int it = 0;
        boolean sucess = false;
        while (!auxQueue.isEmpty() && !sucess) {
            Node headNode = auxQueue.poll();
            it++;
            boolean yahoo = evaluateState(headNode);

            if (yahoo) { // something happened
                if (sucess = headNode.isGoal()) {
                    route = getRouteToNode(headNode);
                    depthOfTheTree = headNode.deepness;
                    totalCost = headNode.cost;
                }
                auxQueue.clear();
                auxQueue.add(headNode);
            } else {
                auxQueue.addAll(headNode.getOffsprings());
            }
        }
        computationTime = (int) (System.currentTimeMillis() - time);
        printSucess(sucess);

        return route;
    }

    public Route depth(Stage stage) {
        long time = System.currentTimeMillis();
        Seeker.stage = stage;
        Route route = new Route();
        Stack<Node> auxStack = new Stack<Node>();
        auxStack.push(new Node(stage.getRobot().getX(), stage.getRobot().getY()));

        int it = 0;
        boolean sucess = false;
        while (!auxStack.isEmpty() && !sucess && it < 100000) {
            Node headNode = auxStack.pop();
            it++;
            boolean yahoo = evaluateState(headNode);
            if (yahoo) { // something happened
                if (sucess = headNode.isGoal()) {
                    route = getRouteToNode(headNode);
                    depthOfTheTree = headNode.deepness;
                    totalCost = headNode.cost;
                }
                auxStack.clear();
                auxStack.add(headNode);
            } else {
                List<Node> nodes = headNode.getOffsprings();
                for (int i = nodes.size() - 1; i >= 0; i--) {
                    if (!auxStack.contains(nodes.get(i))) {
                        auxStack.push(nodes.get(i));
                    }
                }
            }
        }

        computationTime = (int) (System.currentTimeMillis() - time);
        printSucess(sucess);

        return route;
    }

    public Route greedy(Stage stage) {
        long time = System.currentTimeMillis();
        Seeker.stage = stage;
        Route route = new Route();
        PriorityQueue<GreedyNode> auxQueue = new PriorityQueue<GreedyNode>();
        auxQueue.add(new GreedyNode(stage.getRobot().getX(), stage.getRobot().getY()));

        int it = 0;
        boolean sucess = false;
        while (!auxQueue.isEmpty() && !sucess) {
            it++;
            GreedyNode headNode = auxQueue.poll();
            boolean yahoo = evaluateState(headNode);

            if (yahoo) { // something happened
                if (sucess = headNode.isGoal()) {
                    route = getRouteToNode(headNode);
                    depthOfTheTree = headNode.deepness;
                    totalCost = headNode.cost;
                }
                auxQueue.clear();
                auxQueue.add(headNode);
            } else {
                auxQueue.addAll(headNode.getGreedyOffsprings());
            }
        }

        computationTime = (int) (System.currentTimeMillis() - time);
        printSucess(sucess);
        return route;
    }

    public Route aStar(Stage stage) {
        long time = System.currentTimeMillis();
        Seeker.stage = stage;
        Route route = new Route();
        PriorityQueue<AStarNode> auxQueue = new PriorityQueue<AStarNode>();
        auxQueue.add(new AStarNode(stage.getRobot().getX(), stage.getRobot().getY()));

        int it = 0;
        boolean sucess = false;
        while (!auxQueue.isEmpty() && !sucess) {
            it++;
            AStarNode headNode = auxQueue.poll();
            boolean yahoo = evaluateState(headNode);

            if (yahoo) { // something happened
                if (sucess = headNode.isGoal()) {
                    route = getRouteToNode(headNode);
                    depthOfTheTree = headNode.deepness;
                    totalCost = headNode.cost;
                }
                auxQueue.clear();
                auxQueue.add(headNode);
            } else {
                auxQueue.addAll(headNode.getAStarOffsprings());
            }
        }

        computationTime = (int) (System.currentTimeMillis() - time);
        printSucess(sucess);
        return route;
    }

    private boolean evaluateState(Node headNode) {

        boolean yahoo = false;

        switch (headNode.getElement()) {
            case 0:
                break;
            case -2:
                if (!haveItem1 && !item1Right) {
                    haveItem1 = true;
                    loadedWeight += stage.getConfig().getWeightObject1();
                    System.out.println("Item1 encontrado en: [" + headNode.x + "][" + headNode.y + "]");
                    yahoo = true;
                }
                break;
            case -3:
                if (!haveItem2 && !item2Right) {
                    haveItem2 = true;
                    loadedWeight += stage.getConfig().getWeightObject2();
                    System.out.println("Item2 encontrado en: [" + headNode.x + "][" + headNode.y + "]");
                    yahoo = true;
                }
                break;
            case -4:
                if (haveItem1 && !item1Right) {
                    System.out.println("Item1 situado en: [" + headNode.x + "][" + headNode.y + "]");
                    item1Right = true;
                    loadedWeight -= stage.getConfig().getWeightObject1();
                    yahoo = true;
                }
                break;
            case -5:
                if (haveItem2 && !item2Right) {
                    System.out.println("Item2 situado en: [" + headNode.x + "][" + headNode.y + "]");
                    item2Right = true;
                    loadedWeight -= stage.getConfig().getWeightObject1();
                    yahoo = true;
                }

                break;
        }
        return yahoo;
    }

    private void printSucess(boolean sucess) {
        System.out.println("============================");
        if (sucess) {
            System.out.println("Problema solucionado :)");
        } else {
            System.out.println("Problema No solucionado :(");
        }
        System.out.println("NODOS EXPANDIDOS = " + expandedNodes);
        System.out.println("PROFUNDIDAD DEL ARBOL = " + depthOfTheTree);
        System.out.println("TIEMPO DE COMPUTO = " + computationTime + " ms");
        System.out.println("COSTO DE RECORRIDO = " + totalCost);
        System.out.println("============================");
    }
}
